]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Implement a Telepathy Approver and Handler (#643594)
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Tue, 5 Apr 2011 11:28:11 +0000 (13:28 +0200)
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Thu, 9 Jun 2011 11:57:18 +0000 (13:57 +0200)
As the Shell does more than observing channels (users can interact with them),
it has to be an Handler as well. We have to make sure that all the new
incoming text channels are handled by the Shell by default, so we make it an
Approver as well.

From an user point of view, the only difference is that Empathy's tray icon
will stop blicking when receiving new channels.

We rely on ChannelDispatcher.DelegateChannels() and PresentChannel() to
interact with Empathy. Those methods have been implemented in
telepathy-mission-control 5.9.0 and telepathy-glib 0.15.0.

configure.ac
js/ui/telepathyClient.js
src/shell-tp-client.c
src/shell-tp-client.h

index 826070eb64e5ea4a277f84242a204106142a233a..f722c139213e729e026de3742e625a3f4525bbe9 100644 (file)
@@ -73,7 +73,7 @@ GIO_MIN_VERSION=2.25.9
 LIBECAL_MIN_VERSION=2.32.0
 LIBEDATASERVER_MIN_VERSION=1.2.0
 LIBEDATASERVERUI_MIN_VERSION=2.91.6
-TELEPATHY_GLIB_MIN_VERSION=0.13.12
+TELEPATHY_GLIB_MIN_VERSION=0.15.0
 TELEPATHY_LOGGER_MIN_VERSION=0.2.4
 POLKIT_MIN_VERSION=0.100
 STARTUP_NOTIFICATION_MIN_VERSION=0.11
index b1cef1d3a89276e2ec3de9d2277b4c4c6dbf406f..a87ab2ef3869516857e10672629748bd555597b0 100644 (file)
@@ -77,14 +77,20 @@ Client.prototype = {
         // The second argument, recover, means _observeChannels will be run
         // for any existing channel as well.
         let dbus = Tp.DBusDaemon.dup();
-        this._observer = Shell.TpClient.new(dbus);
-        this._observer.set_observe_channels_func(
+        this._tp_client = new Shell.TpClient({ 'dbus_daemon': dbus,
+                                               'name': 'GnomeShell',
+                                               'uniquify-name': true })
+        this._tp_client.set_observe_channels_func(
             Lang.bind(this, this._observeChannels));
+        this._tp_client.set_approve_channels_func(
+            Lang.bind(this, this._approveChannels));
+        this._tp_client.set_handle_channels_func(
+            Lang.bind(this, this._handleChannels));
 
         try {
-            this._observer.register();
+            this._tp_client.register();
         } catch (e) {
-            throw new Error('Couldn\'t register SimpleObserver. Error: \n' + e);
+            throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
         }
     },
 
@@ -135,30 +141,78 @@ Client.prototype = {
         if (this._sources[channel.get_object_path()])
             return;
 
-        let source = new Source(account, conn, channel, contact);
+        let source = new Source(account, conn, channel, contact, this._tp_client);
 
         this._sources[channel.get_object_path()] = source;
         source.connect('destroy', Lang.bind(this,
                        function() {
+                           if (this._tp_client.is_handling_channel(channel)) {
+                               // The chat box has been destroyed so it can't
+                               // handle the channel any more.
+                               channel.close_async(null);
+                           }
+
                            delete this._sources[channel.get_object_path()];
                        }));
+    },
+
+    _handlingChannels: function(account, conn, channels) {
+        let len = channels.length;
+        for (let i = 0; i < len; i++) {
+            let channel = channels[i];
+
+            // We can only handle text channel, so close any other channel
+            if (!(channel instanceof Tp.TextChannel)) {
+                channel.close_async(null);
+                continue;
+            }
+
+            if (this._tp_client.is_handling_channel(channel)) {
+                // We are already handling the channel, display the source
+                let source = this._sources[channel.get_object_path()];
+                if (source)
+                    source.notify();
+            }
+        }
+    },
+
+    _approveChannels: function(approver, account, conn, channels,
+                               dispatchOp, context) {
+        // Approve the channels right away as we are going to handle it
+        dispatchOp.claim_with_async(this._tp_client,
+                                    Lang.bind (this, function(dispatchOp, result) {
+            try {
+                dispatchOp.claim_with_finish(result);
+                this._handlingChannels(account, conn, channels);
+            } catch (err) {
+                global.logError('Failed to Claim channel: ' + err);
+            }}));
+
+        context.accept();
+    },
+
+    _handleChannels: function(handler, account, conn, channels,
+                              requests, user_action_time, context) {
+        this._handlingChannels(account, conn, channels);
+        context.accept();
     }
 };
 
-function Source(account, conn, channel, contact) {
-    this._init(account, conn, channel, contact);
+function Source(account, conn, channel, contact, client) {
+    this._init(account, conn, channel, contact, client);
 }
 
 Source.prototype = {
     __proto__:  MessageTray.Source.prototype,
 
-    _init: function(account, conn, channel, contact) {
+    _init: function(account, conn, channel, contact, client) {
         MessageTray.Source.prototype._init.call(this, contact.get_alias());
 
         this.isChat = true;
 
         this._account = account;
         this._contact = contact;
+        this._client = client;
 
         this._conn = conn;
         this._channel = channel;
@@ -216,13 +270,17 @@ Source.prototype = {
     },
 
     open: function(notification) {
-        let props = {};
-        props[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
-        [props[Tp.PROP_CHANNEL_TARGET_HANDLE], props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE]] = this._channel.get_handle();
-
-        let req = Tp.AccountChannelRequest.new(this._account, props, global.get_current_time());
-
-        req.ensure_channel_async('', null, null);
+          if (this._client.is_handling_channel(this._channel)) {
+              // We are handling the channel, try to pass it to Empathy
+              this._client.delegate_channels_async([this._channel], global.get_current_time(), "", null);
+          }
+          else {
+              // We are not the handler, just ask to present the channel
+              let dbus = Tp.DBusDaemon.dup();
+              let cd = Tp.ChannelDispatcher.new(dbus);
+
+              cd.present_channel_async(this._channel, global.get_current_time(), null);
+          }
     },
 
     _getLogMessages: function() {
index f34000daaf31a619be8c30bbcf83cba175b1af1d..af507bc886e9efa9271c78921e99a770d436e393 100644 (file)
@@ -11,6 +11,14 @@ struct _ShellTpClientPrivate
   ShellTpClientObserveChannelsImpl observe_impl;
   gpointer user_data_obs;
   GDestroyNotify destroy_obs;
+
+  ShellTpClientApproveChannelsImpl approve_channels_impl;
+  gpointer user_data_approve_channels;
+  GDestroyNotify destroy_approve_channels;
+
+  ShellTpClientHandleChannelsImpl handle_channels_impl;
+  gpointer user_data_handle_channels;
+  GDestroyNotify destroy_handle_channels;
 };
 
 /**
@@ -32,23 +40,71 @@ struct _ShellTpClientPrivate
  * Signature of the implementation of the ObserveChannels method.
  */
 
+/**
+ * ShellTpClientApproveChannelsImpl:
+ * @client: a #ShellTpClient instance
+ * @account: a #TpAccount having %TP_ACCOUNT_FEATURE_CORE prepared if possible
+ * @connection: a #TpConnection having %TP_CONNECTION_FEATURE_CORE prepared
+ * if possible
+ * @channels: (element-type TelepathyGLib.Channel): a #GList of #TpChannel,
+ *  all having %TP_CHANNEL_FEATURE_CORE prepared if possible
+ * @dispatch_operation: (allow-none): a #TpChannelDispatchOperation or %NULL;
+ *  the dispatch_operation is not guaranteed to be prepared
+ * @context: a #TpAddDispatchOperationContext representing the context of this
+ *  D-Bus call
+ *
+ * Signature of the implementation of the AddDispatchOperation method.
+ */
+
+/**
+ * ShellTpClientHandleChannelsImpl:
+ * @client: a #ShellTpClient instance
+ * @account: a #TpAccount having %TP_ACCOUNT_FEATURE_CORE prepared if possible
+ * @connection: a #TpConnection having %TP_CONNECTION_FEATURE_CORE prepared
+ * if possible
+ * @channels: (element-type TelepathyGLib.Channel): a #GList of #TpChannel,
+ *  all having %TP_CHANNEL_FEATURE_CORE prepared if possible
+ * @requests_satisfied: (element-type TelepathyGLib.ChannelRequest): a #GList of
+ * #TpChannelRequest having their object-path defined but are not guaranteed
+ * to be prepared.
+ * @user_action_time: the time at which user action occurred, or one of the
+ *  special values %TP_USER_ACTION_TIME_NOT_USER_ACTION or
+ *  %TP_USER_ACTION_TIME_CURRENT_TIME
+ *  (see #TpAccountChannelRequest:user-action-time for details)
+ * @context: a #TpHandleChannelsContext representing the context of this
+ *  D-Bus call
+ *
+ * Signature of the implementation of the HandleChannels method.
+ */
+
 static void
 shell_tp_client_init (ShellTpClient *self)
 {
+  GHashTable *filter;
+
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_TP_CLIENT,
       ShellTpClientPrivate);
 
+  /* We only care about single-user text-based chats */
+  filter = tp_asv_new (
+      TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
+        TP_IFACE_CHANNEL_TYPE_TEXT,
+      TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
+        TP_HANDLE_TYPE_CONTACT,
+      NULL);
+
   /* Observer */
   tp_base_client_set_observer_recover (TP_BASE_CLIENT (self), TRUE);
 
-  /* We only care about single-user text-based chats */
-  tp_base_client_take_observer_filter (TP_BASE_CLIENT (self),
-      tp_asv_new (
-        TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
-          TP_IFACE_CHANNEL_TYPE_TEXT,
-        TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
-          TP_HANDLE_TYPE_CONTACT,
-        NULL));
+  tp_base_client_add_observer_filter (TP_BASE_CLIENT (self), filter);
+
+  /* Approver */
+  tp_base_client_add_approver_filter (TP_BASE_CLIENT (self), filter);
+
+  /* Handler */
+  tp_base_client_add_handler_filter (TP_BASE_CLIENT (self), filter);
+
+  g_hash_table_unref (filter);
 }
 
 static void
@@ -68,6 +124,40 @@ observe_channels (TpBaseClient *client,
       dispatch_operation, requests, context, self->priv->user_data_obs);
 }
 
+static void
+add_dispatch_operation (TpBaseClient *client,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    TpChannelDispatchOperation *dispatch_operation,
+    TpAddDispatchOperationContext *context)
+{
+  ShellTpClient *self = (ShellTpClient *) client;
+
+  g_assert (self->priv->approve_channels_impl != NULL);
+
+  self->priv->approve_channels_impl (self, account, connection, channels,
+      dispatch_operation, context, self->priv->user_data_approve_channels);
+}
+
+static void
+handle_channels (TpBaseClient *client,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    GList *requests_satisfied,
+    gint64 user_action_time,
+    TpHandleChannelsContext *context)
+{
+  ShellTpClient *self = (ShellTpClient *) client;
+
+  g_assert (self->priv->handle_channels_impl != NULL);
+
+  self->priv->handle_channels_impl (self, account, connection, channels,
+      requests_satisfied, user_action_time, context,
+      self->priv->user_data_handle_channels);
+}
+
 static void
 shell_tp_client_dispose (GObject *object)
 {
@@ -79,6 +169,21 @@ shell_tp_client_dispose (GObject *object)
     {
       self->priv->destroy_obs (self->priv->user_data_obs);
       self->priv->destroy_obs = NULL;
+      self->priv->user_data_obs = NULL;
+    }
+
+  if (self->priv->destroy_approve_channels != NULL)
+    {
+      self->priv->destroy_approve_channels (self->priv->user_data_approve_channels);
+      self->priv->destroy_approve_channels = NULL;
+      self->priv->user_data_approve_channels = NULL;
+    }
+
+  if (self->priv->destroy_handle_channels != NULL)
+    {
+      self->priv->destroy_handle_channels (self->priv->user_data_handle_channels);
+      self->priv->destroy_handle_channels = NULL;
+      self->priv->user_data_handle_channels = NULL;
     }
 
   if (dispose != NULL)
@@ -96,22 +201,8 @@ shell_tp_client_class_init (ShellTpClientClass *cls)
   object_class->dispose = shell_tp_client_dispose;
 
   base_clt_cls->observe_channels = observe_channels;
-}
-
-/**
- * shell_tp_client_new:
- * @dbus: a #TpDBusDaemon object, may not be %NULL
- *
- * Returns: a new #ShellTpClient
- */
-ShellTpClient *
-shell_tp_client_new (TpDBusDaemon *dbus)
-{
-  return g_object_new (SHELL_TYPE_TP_CLIENT,
-      "dbus-daemon", dbus,
-      "name", "GnomeShell",
-      "uniquify-name", TRUE,
-      NULL);
+  base_clt_cls->add_dispatch_operation = add_dispatch_operation;
+  base_clt_cls->handle_channels = handle_channels;
 }
 
 void
@@ -127,6 +218,32 @@ shell_tp_client_set_observe_channels_func (ShellTpClient *self,
   self->priv->destroy_obs = destroy;
 }
 
+void
+shell_tp_client_set_approve_channels_func (ShellTpClient *self,
+    ShellTpClientApproveChannelsImpl approve_channels_impl,
+    gpointer user_data,
+    GDestroyNotify destroy)
+{
+  g_assert (self->priv->approve_channels_impl == NULL);
+
+  self->priv->approve_channels_impl = approve_channels_impl;
+  self->priv->user_data_approve_channels = user_data;
+  self->priv->destroy_approve_channels = destroy;
+}
+
+void
+shell_tp_client_set_handle_channels_func (ShellTpClient *self,
+    ShellTpClientHandleChannelsImpl handle_channels_impl,
+    gpointer user_data,
+    GDestroyNotify destroy)
+{
+  g_assert (self->priv->handle_channels_impl == NULL);
+
+  self->priv->handle_channels_impl = handle_channels_impl;
+  self->priv->user_data_handle_channels = user_data;
+  self->priv->destroy_handle_channels = destroy;
+}
+
 /* Telepathy utility functions */
 
 /**
index cec24ccbcd4bfaa85a2db1e98f595edf47e9c053..064c6abe5b9aba67c2b0815355b355a2dbbb8f2d 100644 (file)
@@ -51,13 +51,40 @@ typedef void (*ShellTpClientObserveChannelsImpl) (ShellTpClient *client,
                                                   TpObserveChannelsContext *context,
                                                   gpointer user_data);
 
-ShellTpClient * shell_tp_client_new (TpDBusDaemon *dbus);
-
 void shell_tp_client_set_observe_channels_func (ShellTpClient *self,
                                                 ShellTpClientObserveChannelsImpl observe_impl,
                                                 gpointer user_data,
                                                 GDestroyNotify destroy);
 
+typedef void (*ShellTpClientApproveChannelsImpl) (
+    ShellTpClient *client,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    TpChannelDispatchOperation *dispatch_operation,
+    TpAddDispatchOperationContext *context,
+    gpointer user_data);
+
+void shell_tp_client_set_approve_channels_func (ShellTpClient *self,
+    ShellTpClientApproveChannelsImpl approve_impl,
+    gpointer user_data,
+    GDestroyNotify destroy);
+
+typedef void (*ShellTpClientHandleChannelsImpl) (
+    ShellTpClient *client,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    GList *requests_satisfied,
+    gint64 user_action_time,
+    TpHandleChannelsContext *context,
+    gpointer user_data);
+
+void shell_tp_client_set_handle_channels_func (ShellTpClient *self,
+    ShellTpClientHandleChannelsImpl handle_channels_impl,
+    gpointer user_data,
+    GDestroyNotify destroy);
+
 /* Telepathy utility functions */
 typedef void (*ShellGetTpContactCb) (TpConnection *connection,
                                      GList *contacts,